﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MathematicallySafe.HeadCounter.Data;
using MathematicallySafe.HeadCounter.Data.Common;
using MathematicallySafe.HeadCounter.Data.Models;
using StructureMap;

namespace MathematicallySafe.HeadCounter.Services.Calculators
{
    // todo move this to its proper place and possibly make it configurable.
    public static class LootValueCalculator
    {
        private static readonly IHeadCounterService _headCounterService = ObjectFactory.GetInstance<IHeadCounterService>();

        #region Slot weights

        private static Dictionary<LootSlot, double> SlotWeights = new Dictionary<LootSlot, double>()
                                                                   {
                                                                       {LootSlot.Head, 1.0},
                                                                       {LootSlot.Chest, 1.0},
                                                                       {LootSlot.Legs, 1.0},
                                                                       {LootSlot.TwoHand, 2.0},
                                                                       {LootSlot.Shoulder, 0.75},
                                                                       {LootSlot.Hands, 0.75},
                                                                       {LootSlot.Waist, 0.75},
                                                                       {LootSlot.Feet, 0.75},
                                                                       {LootSlot.Trinket, 0.75},
                                                                       {LootSlot.Wrist, 0.5},
                                                                       {LootSlot.Neck, 0.5},
                                                                       {LootSlot.Back, 0.5},
                                                                       {LootSlot.Finger, 0.5},
                                                                       {LootSlot.OffHand, 0.5},
                                                                       {LootSlot.Relic, 0.5},
                                                                       {LootSlot.OneHand, 1.5},
                                                                       {LootSlot.MainHand, 1.5},
                                                                       {LootSlot.Ranged, 0.5},
                                                                       {LootSlot.Thrown, 0.5},
                                                                       {LootSlot.Other, 0.5}
                                                                   };

        private static Dictionary<LootSlot, double> HunterSlotWeights;
        private static Dictionary<LootSlot, double> TankSlotWeights;
        private static Dictionary<LootSlot, double> DualWieldSlotWeights;

        private static bool ModifedWeightsInitialised()
        {
            return HunterSlotWeights != null;
        }

        private static void InitialiseModifiedWeights()
        {
            if (!ModifedWeightsInitialised())
            {
                // Loosely based on the logic described at http://www.epgpweb.com/help/gearpoints

                HunterSlotWeights = new Dictionary<LootSlot, double>();
                SlotWeights.ForEach(kvp => HunterSlotWeights.Add(kvp.Key, kvp.Value));

                HunterSlotWeights[LootSlot.TwoHand] = 1.0;
                HunterSlotWeights[LootSlot.OneHand] = 0.5;
                HunterSlotWeights[LootSlot.MainHand] = 0.5;
                HunterSlotWeights[LootSlot.Ranged] = 1.5;

                TankSlotWeights = new Dictionary<LootSlot, double>();
                SlotWeights.ForEach(kvp => TankSlotWeights.Add(kvp.Key, kvp.Value));

                TankSlotWeights[LootSlot.OneHand] = 0.5;
                TankSlotWeights[LootSlot.MainHand] = 0.5;
                TankSlotWeights[LootSlot.OffHand] = 1.5;

                DualWieldSlotWeights = new Dictionary<LootSlot, double>();
                SlotWeights.ForEach(kvp => DualWieldSlotWeights.Add(kvp.Key, kvp.Value));

                DualWieldSlotWeights[LootSlot.OneHand] = 1.0;
                DualWieldSlotWeights[LootSlot.MainHand] = 1.0;
            }
        }

        #endregion

        public static double LootValue(this LootItem item, Raider raider)
        {
            InitialiseModifiedWeights();

            var weights = GetSlotWeights(raider);
            return CalculateLootValue(item, weights);
        }

        public static string ValueCalculationDetails(this LootItem item, Raider raider)
        {
            InitialiseModifiedWeights();
            var weights = GetSlotWeights(raider);

            var iLevel = item.iLevel;
            var slot = item.Slot;

            var lootOverride = _headCounterService.Options.Overrides.FirstOrDefault(ov => ov.id == item.ID);

            if (lootOverride != null)
            {
                iLevel = lootOverride.iLevel;
                slot = lootOverride.Slot;
            }

            double iLevelIncrease = iLevel - _headCounterService.Options.iLevelBase;
            var iLevelWeight = iLevelIncrease / _headCounterService.Options.iLevelTierDifference;
            var lootValue = Math.Max(weights[slot]*iLevelWeight, 0.0f);

            return "iLevel " + iLevelWeight.ToString("F2") + " * Slot " + weights[item.Slot] + " = Value " +  lootValue.ToString("F2");
        }

        private static double CalculateLootValue(LootItem item, IDictionary<LootSlot, double> weights)
        {
            // todo refactor duplicate code and tidy this up.
            var iLevel = item.iLevel;
            var slot = item.Slot;

            var lootOverride = _headCounterService.Options.Overrides.FirstOrDefault(ov => ov.id == item.ID);

            if (lootOverride != null)
            {
                iLevel = lootOverride.iLevel;
                slot = lootOverride.Slot;
            }

            double iLevelIncrease = iLevel - _headCounterService.Options.iLevelBase;
            var iLevelWeight = iLevelIncrease / _headCounterService.Options.iLevelTierDifference; 
            return Math.Max(weights[slot] * iLevelWeight, 0.0f);
        }

        private static Dictionary<LootSlot, double> GetSlotWeights(Raider raider)
        {
            if (raider.Class == CharacterClass.Hunter)
            {
                return HunterSlotWeights;
            }

            if (raider.Class == CharacterClass.Rogue || raider.IsDualWield)
            {
                return DualWieldSlotWeights;
            }

            if (raider.IsShieldTank)
            {
                return TankSlotWeights;
            }

            return SlotWeights;
        }

    }
}